﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace JWTAuthorize
{
    /// <summary>
    /// 继承抽象类AuthorizationHandler，AuthorizationHandler又继承了接口IAuthorizationHandler，
    /// 所以可以通过<接口,实现>的方式注入
    /// </summary>
    public class JwtProviderHandler : AuthorizationHandler<CustomRequirement>
    {        
        public IAuthenticationSchemeProvider Schemes { get; set; }
        /// <summary>
        /// 用户权限集合
        /// </summary>
        List<RolePermission> _permissions;
        /// <summary>
        /// 通过构造函数注入
        /// </summary>
        public JwtProviderHandler(IAuthenticationSchemeProvider schemes, List<RolePermission> permissions = null)
        {
            Schemes = schemes;
            _permissions = permissions;
        }

        /// <summary>
        /// 重写抽象类AuthorizationHandler中的HandleRequirementAsync方法
        /// 在此方法中判断用户是否登录，并且从用户权限列表中验证请求的当前URL是否有权限
        /// </summary>
        /// <param name="context"></param>
        /// <param name="requirement"></param>
        /// <returns></returns>
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement)
        {
            //转成HttpContext，取出请求信息，请求信息在请求管道的上下文中
            var httpContext = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;
            //当前请求的Url
            var questUrl = httpContext.Request.Path.Value.ToLower();
            //判断请求是否终止
            var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
            foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
            {
                var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;
                if (handler != null && await handler.HandleRequestAsync())
                {
                    context.Fail();
                    return;
                }
            }
            //判断用户是否登录
            var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
            if (defaultAuthenticate != null)
            {
                var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
                if (result?.Principal != null)
                {
                    httpContext.User = result.Principal;
                    //从权限中查询是否存在请求的url，即是否有当前URL的权限
                    if (_permissions != null && _permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
                    {
                        var name = httpContext.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType).Value;
                        if (_permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() == 0)
                        {
                            //没有权限时跳转到拒绝页面   
                            httpContext.Response.Redirect(requirement.DeniedAction);
                            context.Succeed(requirement);
                            return;
                        }
                    }
                    //有权限时，判断jwt是否过期
                    if (DateTime.Parse(httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration).Value) >= DateTime.Now)
                    {
                        context.Succeed(requirement);
                    }
                    else
                    {
                        context.Fail();
                    }
                    return;
                }
            }
            //判断没有登录时，判断当前访问的URL是否是登录的URL,限制为必须是post请求，必须是form表单请求
            if (!questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST")
               || !httpContext.Request.HasFormContentType))
            {
                context.Fail();
                return;
            }
            context.Succeed(requirement);//设置当前请求的上下文是成功的
        }
    }
}
